namespace SS.Common.Collection
{
    using System;
    using System.Collections.Generic;
    using System.Threading;

    #region Used namespaces

    

    #endregion

    /// <summary>
    /// Provide an synchronous queue by wrapping the real <see cref="Queue{T}"/>
    /// </summary>
    /// <typeparam name="T">
    /// The type of items
    /// </typeparam>
    public sealed class SyncQueue<T> : IDisposable
    {
        #region variables

        /// <summary>
        /// The internal queue
        /// </summary>
        private readonly Queue<T> queue;

        /// <summary>
        /// Used as a notifier signal when there is job in queue
        /// </summary>
        private readonly Semaphore semaphore;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="SyncQueue{T}"/> class. 
        /// Init job queue, lock object and notifier
        /// </summary>
        public SyncQueue()
        {
            // Init queue
            this.queue = new Queue<T>();

            // Init lock object
            this.LockRoot = new object();

            // Init notifier
            // At begining, the queue is empty
            this.semaphore = new Semaphore(0, Int32.MaxValue);
        }

        #endregion

        #region properties

        /// <summary>
        /// Gets lock object
        /// Lock this object will prevent enqueue and dequeue functions
        /// </summary>
        public object LockRoot { get; private set; }

        /// <summary>
        /// Gets number of queued items
        /// </summary>
        public int Count
        {
            get
            {
                lock (this.LockRoot)
                {
                    return this.queue.Count;
                }
            }
        }

        #endregion

        /// <summary>
        /// Put a job to queue
        /// </summary>
        /// <param name="job">
        /// Job object
        /// </param>
        public void Enqueue(T job)
        {
            lock (this.LockRoot)
            {
                // Put job to queue
                this.queue.Enqueue(job);

                // Notify a job is putted to queue
                this.semaphore.Release();
            }
        }

        /// <summary>
        /// Pop a job out of queue
        /// </summary>
        /// <returns>
        /// Job object (can be null)
        /// </returns>
        public T Dequeue()
        {
            var job = default(T);

            try
            {
                // Wait for signal
                this.semaphore.WaitOne();

                lock (this.LockRoot)
                {
                    // Check number items of queue for safer
                    if (this.queue.Count > 0)
                    {
                        // Pop a job from queue
                        job = this.queue.Dequeue();
                    }
                }
            }
            catch (ThreadAbortException)
            {
            }
            catch (ThreadInterruptedException)
            {
            }
            catch (ThreadStateException)
            {
            }

            return job;
        }

        /// <summary>
        /// This function will remove all queued jobs
        /// </summary>
        public void Clear()
        {
            lock (this)
            {
                this.queue.Clear();
            }
        }

        #region IDisposable Members

        /// <summary>
        /// Reference to definition <see cref="IDisposable"/>
        /// </summary>
        public void Dispose()
        {
            // Remove all queued jobs
            this.Clear();
        }

        #endregion
    }
}
